using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using ZedGraph;
using System.Data;

namespace HexterConsulting
{
    class Processor
    {
        public class ExceptionEventArgs : EventArgs
        {
            string message;
            public ExceptionEventArgs(Exception ex)
            {
                message = ex.ToString();
            }
            public override string ToString()
            {
                return message;
            }
        }
        public class ProgressEventArgs:EventArgs
        {
            public ProgressEventArgs(int Current,int Total):base()
            {
                current=Current;
                total=Total;
            }
            private int current=0;
            private int total=0;
            public override string ToString()
            {
                return current.ToString() + " / " + total.ToString();
            }
            public int Current
            {
                get
                {
                    return this.current;
                }
            }
            public int Total
            {
                get
                {
                    return this.Total;
                }
            }
        }
        public event EventHandler<ProgressEventArgs> Progress;
        public event EventHandler<ExceptionEventArgs> Error;

        private const string SUMMARY_QUERY = @"select quantize( TO_TIMESTAMP([{0}], 'MM/dd/yyyy hh:mm:ss.ll' ),{1}) as Time, AVG(to_real([{2}]))as Value,Max(to_real([{2}]))as Maximum,Min(to_real([{2}]))as Minimum INTO {3} FROM {4} where TO_INT(TO_TIMESTAMP([{0}], 'MM/dd/yyyy hh:mm:ss.ll' ))>=SUB(TO_INT(TO_LOCALTIME( SYSTEM_TIMESTAMP())),{5}) GROUP BY Time";
        private const string QUANTIZE_QUERY = @"select quantize( TO_TIMESTAMP([{0}], 'MM/dd/yyyy hh:mm:ss.ll' ),{1}) as Time, {2} INTO {3} FROM {4} where TO_INT(TO_TIMESTAMP([{0}], 'MM/dd/yyyy hh:mm:ss.ll' ))>=SUB(TO_INT(TO_LOCALTIME( SYSTEM_TIMESTAMP())),{5}) GROUP BY Time";

        private const string CHART_QUERY = @"select Time,Value,Maximum,Minimum INTO {0} FROM {1}";

        private PerformanceLog _performanceLog;

        private string _dataDir = "";
        private string _chartDir = "";

        private SummaryInterval[] _intervals = new SummaryInterval[]
                {
                new SummaryInterval("day",300,720,DateUnit.Hour,"HH")
                ,new SummaryInterval("week",1800,10080,DateUnit.Day,"ddd")
                //,new SummaryInterval("month",3600,43200,DateUnit.Day,"dd")
                //,new SummaryInterval("year",43200,525600,DateUnit.Month,"MMM")
                };
        private System.Drawing.Color[] _colors = new Color[] {Color.Blue,Color.Red,Color.Green,Color.Maroon,Color.DarkRed,Color.DarkBlue };

        public struct SummaryInterval
        {
            public int Interval;
            public int Length;
            public string FileNameSuffix;
            public DateUnit DateUnit;
            public string DateAxisFormat;
            public SummaryInterval(string FileSuffix, int Interval, int Length,DateUnit Unit,string DateAxisFormat)
            {
                this.FileNameSuffix= FileSuffix;
                this.Interval = Interval;
                this.Length = Length;
                this.DateUnit = Unit;
                this.DateAxisFormat = DateAxisFormat;
            }
        }

        private void OnProgress(int current, int total)
        {
            if (Progress != null)
            {
                Progress(this, new ProgressEventArgs(current, total));
            }
        }
        private void OnError(Exception ex)
        {
            if (Error != null)
            {
                Error(this, new ExceptionEventArgs(ex));
            }
        }

        public Processor(PerformanceLog PerformanceLog,string DataDirectory,string ChartDirectory)
        {
            _performanceLog = PerformanceLog;
            _dataDir = DataDirectory;
            _chartDir = ChartDirectory;
        }

        private string CleanFilename(string Name)
        {
            return Name.Replace("\\", "").Replace("%", "").Replace("/","").Replace(" " ,"").Replace(":","").Replace("#","");
        }



        private void WriteIndexHTML(string filename, System.Collections.Hashtable filenames)
        {
            System.Text.StringBuilder index = new StringBuilder();
            string counterformat = "<td>{0}<br/><a href='{1}'><img src='{2}'/></a></td>";

            foreach (Category c in _performanceLog.Categories.Values)
            {
                foreach (string Instance in c.InstanceNames)
                {
                index.Append("<tr>");
                    foreach (string Counter in c.CounterNames)
                    {
                        int machinecount = 0;
                        foreach (string MachineName in c.MachineNames)
                        {
                            string fieldName = PerformanceLog.FieldName(MachineName, c.Name, Instance, Counter);
                            if (filenames.Contains(fieldName))
                            {
                                int length = index.Length;
                                index.AppendFormat(counterformat, fieldName, filenames[fieldName].ToString().Replace(_chartDir, ""), CleanFilename(fieldName + "-" + this._intervals[0].FileNameSuffix + ".jpg"));

                                //if (machinecount % 2 != 0)
                                //    index.Append("</tr>");
                                //else
                                //    index.Insert(length, "<tr>");

                                index.Append("\r\n");
                            }
                            machinecount++;
                        }
                    }
                index.Append("</tr>");
                }
                
            }
            index.Insert(0, "<html><head><link rel=\"Stylesheet\" type=\"text/css\" href=\"style.css\" /></head><body><table>");
            index.Append("</table></body></html>");
            System.IO.File.WriteAllText(filename, index.ToString());
        }

        private string CounterToYAxis(string CounterName)
        {
            if (CounterName.ToLower().StartsWith("current"))
            {
                return CounterName.Remove(0, 8);
            }
            else if (CounterName.ToLower().StartsWith("total"))
            {
                return CounterName.Remove(0, 6);
            }
            else if (CounterName.ToLower().StartsWith("maximum"))
            {
                return CounterName.Remove(0, 8);
            }
            return CounterName;
        }
        //private PointPairList Convert(System.Data.DataTable table)
        //{
        //    PointPairList ppl = new PointPairList();
        //    foreach (System.Data.DataRow row in table.Rows)
        //    {
        //        if (row.Table.Columns[0].DataType == typeof(DateTime))
        //        {
        //            ppl.Add(new XDate((DateTime)row[0]), System.Convert.ToDouble(row[1]));
        //        }
        //        else
        //        {
        //            ppl.Add(System.Convert.ToDouble(row[0]), System.Convert.ToDouble(row[1]));
        //        }
        //    }
        //    return ppl;
        //}

        private PointPairList[] Convert(MSUtil.ILogRecordset RecordSet)
        {
            MSUtil.ILogRecord rowLP = null;
            PointPairList[] ppl = new PointPairList[RecordSet.getColumnCount() - 1];
            while (!RecordSet.atEnd())
            {
                rowLP = RecordSet.getRecord();
                double date = new XDate((DateTime)rowLP.getValue(0)).XLDate;
                for (int i = 1; i < RecordSet.getColumnCount(); i++)
                {
                    PointPair pp = new PointPair(date, (double)rowLP.getValue(i));
                    if (ppl[i - 1] == null)
                        ppl[i - 1] = new PointPairList();
                    ppl[i - 1].Add(pp);
                }
                RecordSet.moveNext();
            }
            return ppl;
        }
         
        public void MakeSummaryData()
        {
            MSUtil.LogQueryClassClass lq = new MSUtil.LogQueryClassClass();
            MSUtil.COMCSVInputContextClassClass input = new MSUtil.COMCSVInputContextClassClass();
            MSUtil.COMCSVOutputContextClassClass output = new MSUtil.COMCSVOutputContextClassClass();
            string query = "select top 1 * from " + this.FileNameForQuery(this._performanceLog.Filename) ;
            MSUtil.ILogRecordset results = lq.Execute(query, input);
            string timestampName = results.getColumnName(2);
 
            string filename = System.IO.Path.GetFileName( _performanceLog.Filename);
            string path = System.IO.Path.GetDirectoryName(_performanceLog.Filename)+"\\";

            foreach (SummaryInterval interval in this._intervals)
            {
                System.Text.StringBuilder summaryQuery = new StringBuilder();             

                summaryQuery.AppendFormat("select Quantize(to_timestamp([{0}],'MM/dd/yyyy hh:mm:ss.ll'),{1}) as time", timestampName, interval.Interval);

                for (int i = 3; i < results.getColumnCount(); i++)
                {
                    summaryQuery.AppendFormat(",{1}(coalesce(to_real([{0}]),0.0)) as [{0}]", results.getColumnName(i),"AVG");
                }
                summaryQuery.AppendFormat(" into {0} from {1} where time > to_timestamp('{2}','MM/dd/yyyy hh:mm:ss')  group by time order by time", path + interval.FileNameSuffix + filename, path + filename, ((DateTime)DateTime.Now - TimeSpan.FromMinutes(interval.Length)).ToString("MM-dd-yyyy hh:mm:ss"));

                lq.ExecuteBatch(summaryQuery.ToString(), input, output);
            }
        }

//        public void SummerizeData()
//        {
//            MSUtil.LogQueryClassClass lq = new MSUtil.LogQueryClassClass();
//            MSUtil.COMCSVInputContextClassClass input = new MSUtil.COMCSVInputContextClassClass();
//            MSUtil.COMCSVOutputContextClassClass output = new MSUtil.COMCSVOutputContextClassClass();
//            output.tabs=false;
//            //output.oDQuotes="\";
            
//            string dayQuery;
//            string filename;
//            string fieldname;
//            string instance;

//            foreach (Category c in _performanceLog.Categories.Values)
//            {

//#if FALSE
//                foreach (string MachineName in c.MachineNames)
//                {
//                    foreach (string Instance in c.InstanceNames)
//                    {
//                        if (Instance!=null && Instance.Length > 0)
//                            instance = "(" + Instance + ")";
//                        else
//                            instance = string.Empty;
                    
//                        foreach (string Counter in c.CounterNames)
//                        {
//                            fieldname="\\\\"+MachineName+"\\"+c.Name+instance+"\\"+Counter;
//                            foreach (SummaryInterval si in this._intervals)
//                            {


//                                filename = CleanFilename(fieldname + "-"+ si.FileNameSuffix +".csv");
//                                dayQuery = string.Format(SUMMARY_QUERY,
//                                            _performanceLog.TimeFieldName,
//                                            si.Interval,
//                                            fieldname,//fieldname
//                                            FileNameForQuery( _dataDir + filename),//intofile
//                                            FileNameForQuery( _performanceLog.Filename),//sourcefile
//                                            si.Length );//length
//                                try
//                                {
//                                    lq.ExecuteBatch(dayQuery, input, output);
//                                }
//                                catch { }
//                            }//Summary Interval
//                        }//Counter Names
//                    }//Instance Names
//                }//Machine Names
//#endif
//            foreach (SummaryInterval si in this._intervals)
//            {
//                string fieldlist = "";
//                foreach (string Counter in c.CounterNames)
//                {
                    

//                    foreach (string Instance in c.InstanceNames)
//                    {
//                        if (Instance!=null && !Instance.ToLower().Equals("_total"))
//                        {
//                            string InstanceSumFieldName = string.Empty;

//                            string[] machineSpecificFieldNames = new string[c.MachineNames.Length];
//                            //fieldName = "add(TO_REAL(),TO_REAL()) AS " + instance;
//                            int machinecount = 0;
//                            foreach (string MachineName in c.MachineNames)
//                            {

//                                fieldname = PerformanceLog.FieldName(MachineName, c.Name, Instance, Counter);
//                                if (this._performanceLog.FieldExists(fieldname))
//                                {
//                                    machinecount++;
//                                    machineSpecificFieldNames[machinecount - 1] = string.Format("COALESCE(TO_REAL([{0}]),0.0)", fieldname);
//                                    if (machinecount % 2 == 0 && machinecount > 1)
//                                    {
//                                        if (machinecount > 2)
//                                        {
//                                            InstanceSumFieldName = string.Format("ADD({0},ADD({1},{2}))", InstanceSumFieldName, machineSpecificFieldNames[machinecount - 2], machineSpecificFieldNames[machinecount - 1]);
//                                        }
//                                        else
//                                        {
//                                            InstanceSumFieldName = string.Format("ADD({1},{2})", InstanceSumFieldName, machineSpecificFieldNames[machinecount - 2], machineSpecificFieldNames[machinecount - 1]);
//                                        }
//                                    }
//                                }

//                                }//Machine Names
//                                if (machinecount > 1)//&& machinecount % 2 != 0
//                                {
//                                    if (machinecount > 2)
//                                    {
//                                        InstanceSumFieldName = string.Format("ADD({0},ADD({1},{2}))", InstanceSumFieldName, machineSpecificFieldNames[machinecount - 2], machineSpecificFieldNames[machinecount - 1]);
//                                    }
//                                    else
//                                    {
//                                        InstanceSumFieldName = string.Format("ADD({1},{2})", InstanceSumFieldName, machineSpecificFieldNames[machinecount - 2], machineSpecificFieldNames[machinecount - 1]);
//                                    }

//                                }

//                                if (InstanceSumFieldName.Length > 0)
//                                {
//                                    InstanceSumFieldName = "AVG(" + InstanceSumFieldName + ") AS [" + Instance + Counter + "] ";
//                                    if (fieldlist.Length > 0)
//                                        fieldlist = fieldlist + ",";

//                                    fieldlist += InstanceSumFieldName;

//                                }
//                            }//if Instance != _total
//                        }//Instance Names
//                    }//Counter Names
//                    filename = CleanFilename(c.Name + "-" + si.FileNameSuffix + ".csv");
//                    string query = string.Format(QUANTIZE_QUERY,
//                       _performanceLog.TimeFieldName,
//                       si.Interval,
//                       fieldlist,//fieldname
//                       FileNameForQuery(_dataDir + filename),//intofile
//                       FileNameForQuery(_performanceLog.Filename),//sourcefile
//                       si.Length);//length
//                    //execute statement here
//                    try
//                    {
//                        lq.ExecuteBatch(query, input, output);
//                    }
//                    catch { }
//                }//Summary Interval
//            }//Categories
//        }

        private string FileNameForQuery(string Filename)
        {
            return Filename.Replace(" ", "\u0020").Replace("'", "''");
        }

        private void WriteCounterHTML(string Filename, string CounterName, string MachineName, string CategoryName, string InstanceName, SummaryInterval[] Intervals)
        {
            System.Text.StringBuilder html = new StringBuilder();
            html.Append("<html><head>");
            html.Append("<link rel=\"Stylesheet\" type=\"text/css\" href=\"style.css\" />");
            html.Append("</head><body>");
            html.Append("<table>");
            string fieldname = PerformanceLog.FieldName(MachineName, CategoryName, InstanceName, CounterName);// "\\\\" + MachineName + "\\" + CategoryName + InstanceName + "\\" + CounterName;
            html.AppendFormat("<tr><td class=\"title\">{0} {1} {2} {3} </td></tr>", MachineName, CategoryName, InstanceName, CounterName);
            foreach (SummaryInterval si in Intervals)
            {
                string inputFilename = CleanFilename(fieldname + "-" + si.FileNameSuffix + ".csv");
                string outputFileName = System.IO.Path.GetFileNameWithoutExtension(inputFilename) + ".jpg";
                html.AppendFormat("<tr><td>Last {0} Day(s) / {1} Min Avg</td></tr>", TimeSpan.FromMinutes(si.Length).TotalDays, TimeSpan.FromSeconds(si.Interval).TotalMinutes);
                html.AppendFormat("<tr><td><img src=\"{0}\" /></td></tr>", outputFileName);
            }
            html.Append("</html>");
            html.Append("</body></html>");
            System.IO.File.WriteAllText(Filename, html.ToString());
        }

        private void WriteChart(string Title,string XAxisTitle,string YAxisTitle,int Width,int Height,string Filename,PointPairList[] PointPairList,SummaryInterval summaryInterval)
        {
            bool convertToMBs = false;

            if (YAxisTitle.ToLower().EndsWith(" bytes"))
            {
                convertToMBs = true;
            }

            GraphPane myPane = new GraphPane( new RectangleF( 0, 0, Width, Height),
                     Title, XAxisTitle, YAxisTitle );
            myPane.MarginTop = 30;
            myPane.XAxis.Type = AxisType.Date;
            myPane.XAxis.MajorUnit = summaryInterval.DateUnit;
            myPane.XAxis.Step = 1;
            //myPane.XAxis.MinorUnit = DateUnit - 1;
            myPane.XAxis.IsShowTitle = false;
            myPane.XAxis.IsShowGrid = true;
            myPane.XAxis.TitleFontSpec.Size = 10;
            myPane.XAxis.ScaleFontSpec.Size = 30;
            myPane.XAxis.Scale.ScaleFormat = summaryInterval.DateAxisFormat;
            
            myPane.XAxis.Max = ((IPointList) PointPairList[0])[PointPairList[0].Count-1].X;
            //myPane.XAxis.Min = ((IPointList)PointPairList[0])[0].X;//new XDate( XDate.XLDateToDateTime( myPane.XAxis.Max).Subtract(new TimeSpan(0,0,summaryInterval.Length))).XLDate;
            myPane.XAxis.Min = new XDate( XDate.XLDateToDateTime( myPane.XAxis.Max).Subtract(new TimeSpan(0,summaryInterval.Length,0))).XLDate;

            //myPane.XAxis.si
            
            myPane.YAxis.IsUseTenPower = false;
            
            if (convertToMBs)
            {
                //myPane.YAxis.ScaleMag = 1024 * 1024;
                myPane.YAxis.Title = YAxisTitle.Replace("Bytes", "MBs");                
            }
            myPane.YAxis.ScaleFontSpec.Size = 30;
            myPane.YAxis.IsShowGrid = true;
            myPane.YAxis.TitleFontSpec.Size = 30;
            myPane.YAxis.TitleFontSpec.IsBold = false;
            myPane.Legend.IsVisible = false;
            
            for(int i =0 ;i<PointPairList.Length;i++)
            {
                if (convertToMBs)
                {
                    foreach(PointPair pp in PointPairList[i])
                    {
                        pp.Y = pp.Y / 1024 / 1024;
                    }
                }
                myPane.AddCurve( "Data"+1, PointPairList[i], _colors[i],SymbolType.None);
            }
            

            Bitmap bm = new Bitmap( 1, 1 );
            Graphics g = Graphics.FromImage( bm );
            myPane.AxisChange( g );
            myPane.Image.Save( Filename, System.Drawing.Imaging.ImageFormat.Jpeg);    
        }

        string Scale_ScaleFormatEvent(GraphPane pane, Axis axis, double val, int index)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public void MakeCharts()
        {
            MSUtil.LogQueryClassClass lq = new MSUtil.LogQueryClassClass();
            MSUtil.COMCSVInputContextClassClass input = new MSUtil.COMCSVInputContextClassClass();
            //MSUtil.COMChartOutputContextClassClass output = new MSUtil.COMChartOutputContextClassClass();

            string dayQuery;
           // string inputFilename;
            string outputFileName;
            string fieldname;

            System.Collections.Hashtable counterIndex = new System.Collections.Hashtable();
            System.Collections.Hashtable summaryIndex = new System.Collections.Hashtable();

            foreach (Category c in _performanceLog.Categories.Values)
            {
                foreach (string MachineName in c.MachineNames)
                {
                    foreach (string Instance in c.InstanceNames)
                    {
                        foreach (string Counter in c.CounterNames)
                        {
                            bool dataExists = false;

                            fieldname = PerformanceLog.FieldName(MachineName, c.Name, Instance, Counter);

                            foreach (SummaryInterval si in this._intervals)
                            {
                                //inputFilename = CleanFilename(fieldname + "-" + si.FileNameSuffix + ".jpg");
                                outputFileName = CleanFilename(fieldname + "-" + si.FileNameSuffix + ".jpg"); ;
                                string filename = System.IO.Path.GetFileName( _performanceLog.Filename);
                                string path = System.IO.Path.GetDirectoryName(_performanceLog.Filename)+"\\";
                                string sourceFilename = path + si.FileNameSuffix + filename;
                                //System.IO.Path.GetFileNameWithoutExtension(inputFilename) + ".jpg";
                                dayQuery = string.Format("select Time,[{0}] FROM {1}",
                                            fieldname,        
                                    //--FileNameForQuery(_chartDir + outputFileName),
                                            sourceFilename);//length
                                MSUtil.ILogRecordset recordset = null;
                                try
                                {
                                    recordset = lq.Execute(dayQuery, input);
                                    WriteChart("", "", CounterToYAxis(Counter), 500, 135, _chartDir + outputFileName, Convert(recordset), si);
                                    if (System.IO.File.Exists(_chartDir + outputFileName))
                                        dataExists = true;
                                }
                                catch (Exception ex)
                                {
                                    OnError(ex);
                                }
                            }//Summary Interval
                            if (dataExists)
                            {
                                string filename = _chartDir + CleanFilename(fieldname + ".html");
                                counterIndex.Add(fieldname, filename);
                                WriteCounterHTML(filename, Counter, MachineName, c.Name, Instance, _intervals);
                            }
                        }//Counter Names
                    }//Instance Names
                }//Machine Names
            }//Categories
            WriteIndexHTML(_chartDir + "index.html", counterIndex);
        }
    }
}
